package com.heima.wemedia.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.heima.article.dtos.ApArticleConfigDto;
import com.heima.article.pojos.ApArticle;
import com.heima.article.pojos.ApArticleConfig;
import com.heima.article.pojos.ApCommentRepays;
import com.heima.article.pojos.ApComments;
import com.heima.common.constants.ArticleConstants;
import com.heima.common.constants.CommentConstants;
import com.heima.common.exception.CustomException;
import com.heima.feign.article.ArticleFeignClient;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.wemedia.context.BaseContext;
import com.heima.wemedia.dtos.comment.WmCommentDto;
import com.heima.wemedia.dtos.comment.WmCommentLikeDto;
import com.heima.wemedia.dtos.comment.WmNewsCommentDto;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmUserMapper;
import com.heima.wemedia.pojos.*;
import com.heima.wemedia.service.WmCommentService;
import com.heima.wemedia.vos.comment.WmCommentDetailsVo;
import com.heima.wemedia.vos.comment.WmCommentVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class WmCommentServiceImpl implements WmCommentService {

    @Autowired
    private WmNewsMapper wmNewsMapper;

    @Autowired
    private WmUserMapper wmUserMapper;

    @Autowired
    private ArticleFeignClient articleFeignClient;

    @Autowired
    private MongoTemplate mongoTemplate;
    private WmCommentLikeDto wmCommentLikeDto;

    /**
     * 查看评论列表
     *
     * @param wmNewsCommentDto 前端dto
     * @return 返回结果
     */
    @Override
    public Page<WmCommentVo> findNewsComments(WmNewsCommentDto wmNewsCommentDto) {
        //1 非空校验
        if (wmNewsCommentDto == null ||
                wmNewsCommentDto.getPage() == null ||
                wmNewsCommentDto.getSize() == null
        ) {
            throw new CustomException(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        //2 校验是否登录
        Long userId = BaseContext.getCurrentId();
        if (userId == null) {
            throw new CustomException(AppHttpCodeEnum.NEED_LOGIN);
        }
        Integer page = wmNewsCommentDto.getPage();
        Integer size = wmNewsCommentDto.getSize();
        //解析日期型字符串 例如:"2024-05-01"==>转为Date类型
        Date beginDate = null;
        Date endDate = null;
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            beginDate = sdf.parse(wmNewsCommentDto.getBeginDate());
            endDate = sdf.parse(wmNewsCommentDto.getEndDate());
        } catch (ParseException e) {
            e.printStackTrace();
        }
        //3 分页查询 自媒体文章信息
        Page<WmNews> wmNewsPage = new Page<>(page, size);
        //倒序查询发布文章
        LambdaQueryWrapper<WmNews> wrapper = new LambdaQueryWrapper<>();
        wrapper.gt(WmNews::getPublishTime, beginDate)
                .lt(WmNews::getPublishTime, endDate)
                .eq(WmNews::getStatus, WmNews.Status.PUBLISHED.getCode())
                .orderByDesc(WmNews::getPublishTime);
        wmNewsMapper.selectPage(wmNewsPage, wrapper);
        List<WmNews> wmNewsList = wmNewsPage.getRecords();

        //4 重点最后处理返回结果(评论总量和评论状态)
        List<WmCommentVo> commentVoList = getWmNewsCommentVos(wmNewsList);

        // 使用MongoDB查询自媒体文章的评论回复数量 ()
        //5 封装最后返回结果
        Page<WmCommentVo> commentVoPage = new Page<>();
        commentVoPage.setCurrent(page);
        commentVoPage.setSize(size);
        commentVoPage.setTotal(wmNewsPage.getTotal());
        commentVoPage.setRecords(commentVoList);
        return commentVoPage;
    }

    /**
     * 处理向前端返回的结果
     *
     * @param wmNewsList 自媒体文章
     * @return 返回处理后结果
     */
    private List<WmCommentVo> getWmNewsCommentVos(List<WmNews> wmNewsList) {
        ArrayList<WmCommentVo> wmCommentVos = new ArrayList<>();
        for (WmNews wmNews : wmNewsList) {
            Long articleId = wmNews.getArticleId();
            String title = wmNews.getTitle();
            Date createdTime = wmNews.getCreatedTime();

            //查看发布的文章是否是下架状态,如果是下架状态,不查询该文章的评论,不存入向前端返回的结果当中
            ApArticleConfigDto apArticleConfigDto = new ApArticleConfigDto();
            apArticleConfigDto.setArticleId(articleId);
            ApArticleConfig apArticleConfig = getApArticleConfig(apArticleConfigDto);
            if (apArticleConfig.getIsDown() == ArticleConstants.ARTICLE_DOWN_STATUS) {
                continue;
            }

            WmCommentVo wmCommentVo = new WmCommentVo();
            wmCommentVo.setId(articleId.toString());
            wmCommentVo.setTitle(title);
            wmCommentVo.setCreatedTime(createdTime.getTime());
            //4.1 设置每一条文章的评论状态
            //获取评论状态
            Boolean isComment = (apArticleConfig.getIsComment() == CommentConstants.COMMENT_IS_OK);
            wmCommentVo.setIsComment(isComment);
            //4.2 设置每一条文章的评论总量(文章评论量+文章评论回复量)
            //获取每篇文章的评论量
            int comments = getArticleComments(articleId);
            wmCommentVo.setComments(comments);
            wmCommentVos.add(wmCommentVo);
        }
        return wmCommentVos;
    }

    /**
     * 获取文章配置信息
     *
     * @param apArticleConfigDto 条件dto
     * @return 返回结果
     */
    private ApArticleConfig getApArticleConfig(ApArticleConfigDto apArticleConfigDto) {
        ResponseResult<ApArticleConfig> result = articleFeignClient.getArticleConfig(apArticleConfigDto);
        if (result.getCode() != AppHttpCodeEnum.SUCCESS.getCode()) {
            throw new CustomException(AppHttpCodeEnum.CALL_INTERFACE_FAIL);
        }
        return result.getData();
    }

    /**
     * 统计每篇自媒体发布至app端文章的评论量<br>
     * 评论量=文章评论量+文章评论回复量
     *
     * @param articleId 文章id
     * @return 返回app端文章的评论总量
     */
    private int getArticleComments(Long articleId) {
        int comments = 0;
        Query apCommentQuery = Query.query(
                Criteria.where("entryId").is(articleId)
        );
        //每篇文章的评论总量初始0
        List<ApComment> apComments = mongoTemplate.find(apCommentQuery, ApComment.class);
        if (apComments.size() != 0) {
            for (ApComment apComment : apComments) {
                String commentId = apComment.getId();
                comments += 1;
                //查询每条评论的回复量
                Query commentRepayQuery = Query.query(
                        Criteria.where("commentId").is(commentId)
                );
                List<ApCommentRepay> apCommentRepays = mongoTemplate.find(commentRepayQuery, ApCommentRepay.class);
                if (apCommentRepays.size() != 0) {
                    // 不遍历了,直接用当前评论id查询出来的所有评论回复量相加,利于效率提高
                    comments = comments + apCommentRepays.size();
                }
            }
        }
        return comments;
    }

    /**
     * 打开或关闭评论
     *
     * @param wmCommentDto 前端dto
     */
    @Override
    public void updateCommentStatus(WmCommentDto wmCommentDto) {
        //1 参数校验
        if (wmCommentDto == null ||
                wmCommentDto.getArticleId() == null ||
                wmCommentDto.getOperation() == null
        ) {
            throw new CustomException(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        Long userId = BaseContext.getCurrentId();
        //2 校验是否登录
        if (userId == null) {
            throw new CustomException(AppHttpCodeEnum.NEED_LOGIN);
        }

        Integer operation = wmCommentDto.getOperation();//0关闭评论 1开启评论
        String articleId = wmCommentDto.getArticleId();
        ApArticleConfigDto apArticleConfigDto = new ApArticleConfigDto();
        apArticleConfigDto.setArticleId(Long.valueOf(articleId));
        apArticleConfigDto.setIsComment(operation);

        //5 已发布未下架文章-修改文章是否可评论
        ResponseResult result = articleFeignClient.updateArticleConfig(apArticleConfigDto);
        if (result.getCode() != AppHttpCodeEnum.SUCCESS.getCode()) {
            throw new CustomException(AppHttpCodeEnum.CALL_INTERFACE_FAIL);
        }

        //6关闭或开启评论(更新flag字段)
        updateApCommentFlag(articleId, operation);
    }

    /**
     * 删除或回复文章评论(更新flag字段)
     *
     * @param articleId 文章id
     * @param operation 0关闭评论 1开启评论
     */
    private void updateApCommentFlag(String articleId, Integer operation) {
        // 删除该文章中所有评论
        //查询条件
        Query queryComment = Query.query(
                Criteria.where("entryId").is(Long.valueOf(articleId))
        );
        Update update = new Update();
        //更新条件
        if (operation == CommentConstants.COMMENT_IS_OK) {
            update = Update.update("flag", CommentConstants.COMMENT_IS_NO);
        } else if (operation == CommentConstants.COMMENT_IS_NO) {
            update = Update.update("flag", CommentConstants.COMMENT_DELETE_OK);
        }
        //批量更新
        mongoTemplate.updateMulti(queryComment, update, ApComment.class);
    }

    /**
     * 查看文章的具体评论和评论回复信息
     *
     * @param wmNewsCommentDto 查询条件dto
     * @return 返回结果
     */
    @Override
    public List<WmCommentDetailsVo> list(WmNewsCommentDto wmNewsCommentDto) {
        //1 参数校验
        if (wmNewsCommentDto == null ||
                wmNewsCommentDto.getArticleId() == null ||
                wmNewsCommentDto.getPage() == null ||
                wmNewsCommentDto.getSize() == null
        ) {
            throw new CustomException(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        //2 验证是否登录
        Long userId = BaseContext.getCurrentId();
        if (userId == null) {
            throw new CustomException(AppHttpCodeEnum.NEED_LOGIN);
        }

        String articleId = wmNewsCommentDto.getArticleId();
        Integer page = wmNewsCommentDto.getPage();//页码
        Integer size = wmNewsCommentDto.getSize();//显示条数

        //3 分页查询文章评论和文章回复信息
        //先分页查询文章评论,然后遍历文章评论,设置每篇评论的回复列表
        Query queryApComment = Query.query(
                        Criteria.where("entryId").is(Long.valueOf(articleId))
                                .and("flag").is(CommentConstants.COMMENT_IS_NO)
                )//查询条件
                .with(Sort.by(Sort.Direction.DESC, "createdTime"))
                .skip((long) (page - 1) * size)//分页条件:起始索引
                .limit(size);//分页条件:每页显示条数
        List<ApComment> apCommentList = mongoTemplate.find(queryApComment, ApComment.class);
        if (apCommentList.size() == 0) {
            return null;
        }

        //4 转换和封装前端的返回结果wmCommentDetailsVos
        List<WmCommentDetailsVo> wmCommentDetailsVos = getWmCommentDetailsVos(userId, articleId, size, apCommentList);


        return wmCommentDetailsVos;
    }

    /**
     * 封装前端的返回结果wmCommentDetailsVos
     *
     * @param userId        用户id
     * @param articleId     文章id
     * @param size          评论回复条数
     * @param apCommentList 文章评论列表
     * @return 返回结果
     */
    private List<WmCommentDetailsVo> getWmCommentDetailsVos(Long userId,
                                                            String articleId,
                                                            Integer size,
                                                            List<ApComment> apCommentList) {
        List<WmCommentDetailsVo> wmCommentDetailsVos = apCommentList.stream().map(apComment -> {
            String commentId = apComment.getId();
            Date createdTime = apComment.getCreatedTime();
            //转换的结果类型
            WmCommentDetailsVo wmCommentDetailsVo = new WmCommentDetailsVo();
            //4.1 封装文章评论信息和其它字段信息
            ApComments apComments = getApComments(userId, articleId, apComment, commentId, createdTime);
            wmCommentDetailsVo.setApComments(apComments);

            //4.2 封装文章评论回复列表(只查询最新的前10条回复记录)
            List<ApCommentRepays> apCommentRepaysList = getApCommentRepays(size, commentId);
            wmCommentDetailsVo.setApCommentRepays(apCommentRepaysList);

            return wmCommentDetailsVo;
        }).collect(Collectors.toList());
        return wmCommentDetailsVos;
    }

    /**
     * 获取文章评论信息和其它字段信息
     *
     * @param userId      用户id
     * @param articleId   文章id
     * @param apComment   文章评论信息
     * @param commentId   评论id
     * @param createdTime 创建时间
     * @return 返回结果
     */
    private ApComments getApComments(Long userId,
                                     String articleId,
                                     ApComment apComment,
                                     String commentId,
                                     Date createdTime) {
        ApComments apComments = new ApComments();
        //4.1.1拷贝相同字段
        BeanUtils.copyProperties(apComment, apComments);
        //4.1.2处理其他字段
        //likes字段(点赞量0-10)
        apComments.setLikes(apComment.getLikes() >= 10 ? 10 : apComment.getLikes());
        //repay字段(回复量0-10)
        apComments.setReply(apComments.getReply() >= 10 ? 10 : apComments.getReply());
        //image字段(远程调用app微服务,查询文章图片)
        ResponseResult<ApArticle> rs = articleFeignClient.getArticle(Long.valueOf(articleId));
        if (rs.getCode() != AppHttpCodeEnum.SUCCESS.getCode()) {
            throw new CustomException(AppHttpCodeEnum.CALL_INTERFACE_FAIL);
        }
        if (rs.getData() != null) {
            ApArticle apArticle = rs.getData();
            apComments.setImage(apArticle.getImages());
        }
        //ord字段(感觉没有用,设为null)
        apComments.setOrd(null);
        //createdTime字段(评论发布时间)
        apComments.setCreatedTime(createdTime.getTime());
        //updateTime字段(没有评论修改时间,设为null)
        apComments.setUpdateTime(null);
        //status字段(不清楚干啥的,设为null)
        apComments.setStatus(null);
        //operation字段(点赞状态 0点赞 1未点赞)
        //查看当前自媒体用户是否对文章评论点赞
        ApCommentLike apCommentLike = getApCommentLike(userId, commentId);
        if (apCommentLike != null) {
            apComments.setOperation(Integer.parseInt(CommentConstants.LIKE_OPERATION.toString()));
        } else {
            apComments.setOperation(Integer.parseInt(CommentConstants.CANCEL_LIKE_OPERATION.toString()));
        }
        return apComments;
    }

    /**
     * 评论回复
     *
     * @param wmNewsCommentDto 前端dto
     */
    @Override
    public void commentRepay(WmNewsCommentDto wmNewsCommentDto) {
        //参数校验
        if (wmNewsCommentDto == null ||
                wmNewsCommentDto.getCommentId() == null ||
                wmNewsCommentDto.getContent() == null) {
            throw new CustomException(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        //验证是否登录
        Long userId = BaseContext.getCurrentId();
        if (userId == null) {
            throw new CustomException(AppHttpCodeEnum.NEED_LOGIN);
        }
        //保存评论
        String commentId = wmNewsCommentDto.getCommentId();
        String content = wmNewsCommentDto.getContent();
        ApCommentRepay apCommentRepay = new ApCommentRepay();
        apCommentRepay.setAuthorId(userId.intValue());
        WmUser wmUser = wmUserMapper.selectById(userId.intValue());
        apCommentRepay.setAuthorName(wmUser.getName());
        apCommentRepay.setCommentId(commentId);
        apCommentRepay.setContent(content);
        apCommentRepay.setLikes(0);
        apCommentRepay.setCreatedTime(new Date());
        apCommentRepay.setUpdateTime(new Date());
        mongoTemplate.save(apCommentRepay);

    }

    /**
     * 评论点赞
     *
     * @param wmCommentLikeDto 前端dto条件
     * @return 返回结果
     */
    @Override
    public Map<String, Integer> like(WmCommentLikeDto wmCommentLikeDto) {
        this.wmCommentLikeDto = wmCommentLikeDto;
        //参数校验
        if (wmCommentLikeDto == null ||
                wmCommentLikeDto.getOperation() == null ||
                wmCommentLikeDto.getCommentId() == null
        ) {
            throw new CustomException(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        //校验是否登录
        Long userId = BaseContext.getCurrentId();
        if (userId == null) {
            throw new CustomException(AppHttpCodeEnum.NEED_LOGIN);
        }
        String commentId = wmCommentLikeDto.getCommentId();
        Integer operation = wmCommentLikeDto.getOperation();
        int likes = 0;//记录文章点赞量
        //获取点赞或取消点赞后的点赞量
        likes = getCommentLike(userId, commentId, operation, likes);
        likes = getCancelCommentLike(userId, commentId, operation, likes);
        //封装返回数据
        HashMap<String, Integer> data = new HashMap<>();
        data.put("likes", likes);
        return data;
    }


    /**
     * 获取评论点赞量并保存点赞记录和更新点赞数量
     *
     * @param userId    点赞用户
     * @param commentId 点赞文章
     * @param operation 点赞行为: 0点赞 1取消点赞
     * @param likes     保存点赞量
     * @return 返回点赞量
     */
    private int getCommentLike(Long userId, String commentId,
                               Integer operation, int likes) {
        if (operation == CommentConstants.LIKE_OPERATION.intValue()) {
            //查看点赞记录是否存在
            ApCommentLike apCommentLike = getApCommentLike(userId, commentId);
            if (apCommentLike == null) {
                apCommentLike = new ApCommentLike();
                apCommentLike.setCommentId(commentId);
                apCommentLike.setAuthorId(userId.intValue());
                mongoTemplate.save(apCommentLike);
            }
            //评论点赞,点赞量+1
            ApComment apComment = getApComment(commentId);
            if (apComment != null) {
                likes = apComment.getLikes();
                likes += 1;
                apComment.setLikes(likes);
                mongoTemplate.save(apComment);
            }
        }
        return likes;
    }

    /**
     * 获取取消评论点赞量并保存点赞记录和更新点赞数量
     *
     * @param userId    点赞用户
     * @param commentId 点赞文章
     * @param operation 点赞行为: 0点赞 1取消点赞
     * @param likes     保存取消点赞量
     * @return 返回取消点赞量
     */
    private int getCancelCommentLike(Long userId, String commentId,
                                     Integer operation, int likes) {
        if (operation == CommentConstants.CANCEL_LIKE_OPERATION.intValue()) {
            //删除点赞记录
            ApCommentLike apCommentLike = getApCommentLike(userId, commentId);
            if (apCommentLike != null) {
                removeApCommentLike(userId.intValue(), commentId);
            }
            //点赞量-1
            ApComment apComment = getApComment(commentId);
            if (apComment != null) {
                likes = apComment.getLikes();
                likes = likes <= 0 ? 0 : likes - 1;
                apComment.setLikes(likes);
                mongoTemplate.save(apComment);
            }
        }
        return likes;
    }

    /**
     * 获取文章评论
     *
     * @param commentId 评论id
     * @return 返回文章评论信息
     */
    private ApComment getApComment(String commentId) {
        Query queryComment = Query.query(
                Criteria.where("_id").is(commentId)
        );
        ApComment apComment = mongoTemplate.findOne(queryComment, ApComment.class);
        return apComment;
    }

    /**
     * 获取文章评论点赞
     *
     * @param userId    用户id
     * @param commentId 点赞
     * @return 返回评论点赞记录
     */
    private ApCommentLike getApCommentLike(Long userId, String commentId) {
        Query queryCommentLike = Query.query(
                Criteria.where("commentId").is(commentId)
                        .and("authorId").is(userId.intValue())
        );
        //评论点赞,保存点赞记录
        return mongoTemplate.findOne(queryCommentLike, ApCommentLike.class);
    }

    /**
     * 根据用户id和评论id,删除点赞记录
     *
     * @param userId    用户id
     * @param commentId 评论id
     */
    private void removeApCommentLike(int userId, String commentId) {
        Query queryCommentLike = Query.query(
                Criteria.where("commentId").is(commentId)
                        .and("authorId").is(userId)
        );
        mongoTemplate.remove(queryCommentLike, ApCommentLike.class);
    }

    /**
     * 获取文章评论回复列表
     *
     * @param size      显示条数
     * @param commentId 评论id
     * @return 返回结果
     */
    private List<ApCommentRepays> getApCommentRepays(Integer size, String commentId) {
        Query queryApCommentRepay = Query.query(
                        Criteria.where("commentId").is(commentId)
                )
                .with(Sort.by(Sort.Direction.DESC, "createdTime"))
                .limit(size);
        List<ApCommentRepay> apCommentRepayList = mongoTemplate.find(queryApCommentRepay, ApCommentRepay.class);
        if (apCommentRepayList.size() == 0) {
            return null;
        }
        List<ApCommentRepays> apCommentRepaysList = apCommentRepayList.stream().map(apCommentRepay -> {
            ApCommentRepays apCommentRepays = new ApCommentRepays();
            BeanUtils.copyProperties(apCommentRepay, apCommentRepays);
            //处理其他字段
            apCommentRepays.setCreatedTime(apCommentRepay.getCreatedTime().getTime());
            apCommentRepays.setUpdateTime(null);
            return apCommentRepays;
        }).collect(Collectors.toList());
        return apCommentRepaysList;
    }

    /**
     * 删除评论
     *
     * @param commentId 评论id
     */
    @Override
    public void delComment(String commentId) {
        //校验参数
        if (commentId == null) {
            throw new CustomException(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        //校验是否登录
        Long userId = BaseContext.getCurrentId();
        if (userId == null) {
            throw new CustomException(AppHttpCodeEnum.NEED_LOGIN);
        }
        //删除评论同时全部删除评论回复
        //删除评论-更新flag=1
        ApComment apComment = getApComment(commentId);
        if (apComment != null) {
            apComment.setFlag(CommentConstants.COMMENT_DELETE_OK);
            log.info("成功删除文章评论:{}", apComment);
            mongoTemplate.save(apComment);
        }
        //查询该评论下的所有回复并删除
        Query queryCommentRepay = Query.query(
                Criteria.where("commentId").is(commentId)
        );
        List<ApCommentRepay> commentRepays = mongoTemplate.findAllAndRemove(queryCommentRepay, ApCommentRepay.class);
        if (commentRepays.size() != 0) {
            for (ApCommentRepay commentRepay : commentRepays) {
                log.info("成功删除文章评论回复:{}", commentRepay);
            }
        }

    }

    /**
     * 删除评论回复
     *
     * @param commentRepayId 评论回复id
     */
    @Override
    public void delCommentRepay(String commentRepayId) {
        //校验参数
        if (commentRepayId == null) {
            throw new CustomException(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        //校验是否登录
        Long userId = BaseContext.getCurrentId();
        if (userId == null) {
            throw new CustomException(AppHttpCodeEnum.NEED_LOGIN);
        }
        Query query = Query.query(
                Criteria.where("_id").is(commentRepayId)
        );
        ApCommentRepay apCommentRepay = mongoTemplate.findOne(query, ApCommentRepay.class);
        if (apCommentRepay != null) {
            mongoTemplate.remove(query, ApCommentRepay.class);
            log.info("成功删除评论回复:{}", apCommentRepay);
        }
    }
}
